// ToDo List
// ---------
// ToDo 1: Get auto shrink-wrapping of contents working for IE, (current work-around is to force to default size)
// ToDo 2: Fix the explicit adding of the 6px content padding and 1px bar border when calculating "dialog.style.height"
// ToDo 3: Try and optimise the code further for size 
// ToDo 4: Make the title bar "draggable"


// Change Log
// ----------
// Version 1.7.1 - Changes (minified size =  5403 bytes)
// ---------------------
// 1. Fix: Created a globally accessible reference to the showDialog function's buttonList argument so that it can be accessed in hideDialog() as per chilledflame's bug report
//
// Version 1.7 - Changes (minified size =  5330 bytes)
// ---------------------
// 1. Added support for any number of user-defined buttons
// ***Note: The first button's ID will now be 0, the next 1 and so on.

// Version 1.6.1 - (No public release)
// ---------------------
// 1. Fix: ALLOWHIDE test in hideDialog() function should be "if (ALLOWHIDE || buttons == 1 || (buttons == 2  && buttonId != 1) || (buttons == 3 && buttonId != 1) || (buttons == 4 && isDef(button1) && isDef(button2) && buttonId != 1))"

// Version 1.6 - Changes (minified size =  5446 bytes)
// ---------------------
// 1. Fix: ALLOWHIDE test in hideDialog() function should have been "if (ALLOWHIDE || buttonId >= 1)" (as per Maramor's suggestion)
// 2. Added MASKCOLOR, (default value set to #D0D0D0). This is a global variable to set the Dialog mask color (as per Maramor's suggestion)


// Version 1.5 - Changes (minified size =  5347 bytes)
// ---------------------
// 1. Fix: DLGRESULT is now set to the button number when button is clicked. (Close = 0, Button1 = 1, Button2 = 2)
// 2. Fix: Dialog mask style is now set to 'block' the first time the dialog is created, (if the dialog style is modal)
// 3. Fix: Dialog now positions correctly at the cursor position in Internet Explorer when (atcursor == true)
// 4. Fix: Fixed CSS padding and margins for header
// 5. Fix: Now renders correctly in both HTML and XHTML doctypes
// 6. Added ALLOWHIDE to provide support for a persistent dialog, (if ALLOWHIDE == false then Dialog will not hide on a button click. in the case of an invalid input value, the dialog can remain visible)

// Version 1.4 - Changes (minified size =  5271 bytes)
// --------------------- 
// 1. Fix: DLGRESULT is now initialised to null in showDialog().
// 2. Fix: DLGRESULT is now set to false when button2 is clicked.

// Version 1.3 - Changes (minified size = 5272 bytes)
// --------------------- 
// 1. Fix: Included scrolled page position when calculating dialog position
// 2. Fix: Dialog Mask wasn't created for non-modal display and thus was not available for modal display. It is now always created.
// 3: Fix: Fixed the 40 pixel hack to set the correct mask height
// 4: Fix: Fixed the IE6 40 pixel hack to set the correct mask width so that it covers the entire background content
// 5. WRAPPER is now initialised to an empty string which will default to the document.body

// Version 1.2 - Changes (minified size = 4581 bytes)
// --------------------- 
// 1. Show "Close Dialog" button in auto-hide mode
// 2. Added DEFAULT_WIDTH and DEFAULT_CONTENT_HEIGHT global variables
// 3. Added code to prevent dialog being clipped by WRAPPER Right and Bottom boundaries

// Version 1.1 - Changes (minified size = 4484 bytes)
// --------------------- 
// 1. Rewrote dialog sizing and positioning
// 2. Added IE detection to ignore auto sizing for IE
// 3. Removed unneccessary functions 

// Version 1.0 - Changes (minified size = 5215 bytes)
// ---------------------
// 1. Fix: Use an <iframe> for the mask to cover <select> elements in IE <8
// 2. Fix: Replaced hyphens with underscores in CSS class names
// 3. Fix: WRAPPER should contain a valid id of a div or similar element which contains the page content
// 4. Added support for standard and user-defined buttons
// 5. Added support for width and height, (user-defined, default and auto)
// 6. Added support for open-dialog-at-cursor
// 7. Added support for non-modal dialogs
// 8. Added support for click-background-mask-to-close
// 9. Added a DLGRESULT variable set to true or false depending on which button clicked
// 10. Added a "message" dialog box type
// 11. Added a "query" dialog box type


// Notes:
// ------
// Dialog Types
//  error
//  warning
//  success
//  prompt
//  message
//  query

// width/height = null : Default Size
// width/height = 0    : Auto Size to contents 

// Button definitions are now passed into the "showDialog()" function as button object element's in the "buttonList" array
// The application must provide definitions for all buttons that will be displayed as follows:
// Each button object has two properties: {name, close}
// "name" is the displayed text string in the button
// "close" is a boolean value that indicates if the button will force the dialog to close when clicked
// If "close" is false then the global "ALLOWHIDE" parameter determines wether or not the dialog will be closed.
// *Note: The first button's ID will now be 0, the next 1 and so on.

// Example with Submit/Cancel buttons: 
//  showDialog('Title', 'Message', 'message', 0, 0, false, true, true, new Array(new button('Submit', false), new button('Ignore', true)));

                 
// global variables //
var TIMER = 5;
var SPEED = 1000;
var WRAPPER = '';  // defaults to document.body
var DLGRESULT = null;
var ALLOWHIDE = true;  // Support for a persistent dialog
var DEFAULT_WIDTH = 425;
var DEFAULT_CONTENT_HEIGHT = 160;
var MASKCOLOR = '#DDD';

// sniff browser, (content shrink-wrapping doesn't work for IE)
var isIE = (navigator.appName == 'Microsoft Internet Explorer');

// Add an onMouseMove event to track mouse position
if (window.attachEvent) document.attachEvent("onmousemove",MouseMv);
else document.addEventListener("mousemove",MouseMv,false);

function MouseMv(e) 
{
 if (!e) e = window.event;
 if (typeof e.pageX == "number") MouseMv.X = e.pageX;
 else MouseMv.X = e.clientX;

 if (typeof e.pageY == "number") MouseMv.Y = e.pageY;
 else MouseMv.Y = e.clientY;
}
MouseMv.X = 0;
MouseMv.Y = 0;

// check if argument is null, not defined, or of the specified type
function isDef(arg, argtype)
{
 try
 {
  if (argtype === null || typeof(argtype) == 'undefined') return (arg !== null && typeof(arg) != 'undefined');
  return (arg !== null && typeof(arg) == argtype);
 }
 catch (e) {}
 return false;
}

// wrapper for getElementById
function el(id)
{
 try
 {
  if (!isDef(id, 'string') || id == '') return null;
  return document.getElementById(id);
 }
 catch(e) {}
 return null;
}

// get an element's rendered visible content's width
function getWidth(elem)
{
 try
 {
  if (!isDef(elem)) return 0;
  if (elem.style.display == 'none') return 0;
  return elem.innerWidth ? elem.innerWidth :elem.clientWidth ? elem.clientWidth : elem.offsetWidth;
 }
 catch(e) {}

 return 0;
}

// get an element's rendered visible content's height
function getHeight(elem)
{
 try
 {
  if (!isDef(elem)) return 0;
  if (elem.style.display == 'none') return 0;
  return elem.innerHeight ? elem.innerHeight :elem.clientHeight ? elem.clientHeight : elem.offsetHeight;
 }
 catch(e) {}

 return 0;
}

// get scrolled page size
function getScrollSize()
{
 try
 {
  var db = document.body;
  var dde = document.documentElement;
 
  var scrollHeight = Math.max(db.scrollHeight, dde.scrollHeight, db.offsetHeight, dde.offsetHeight, db.clientHeight, dde.clientHeight)
  var scrollWidth = Math.max(db.scrollWidth, dde.scrollWidth, db.offsetWidth, dde.offsetWidth, db.clientWidth, dde.clientWidth)
  
  return {x: scrollWidth, y: scrollHeight};
 }
 catch(e) {}

 return {x: 0, y: 0};
}

// get scrolled page position
function getScrollPos()
{
 try
 {
  if (typeof(pageXOffset) != 'undefined') {return {x: pageXOffset, y: pageYOffset};}  // IE throws an exception if I use "!isDef(pageXOffset)"
  else 
  {
   var db = document.body;
   var dde = document.documentElement;
   return ((dde.clientHeight) ? {x: dde.scrollLeft, y: dde.scrollTop} : {x: db.scrollLeft, y: db.scrollTop});
  }
 }
 catch(e) {}

 return {x: 0, y: 0};
}

// Button object
function button(name, close)
{
 this.name = name;
 this.close = close;
}

// build/show the dialog box, populate the data and call the fadeDialog function //
function showDialog(title, message, type, width, height, autohide, atcursor, modal, buttonList) 
{
 var dialog;
 var dialogheader;
 var dialogclose;
 var dialogtitle;
 var dialogbody;
 var dialogcontent;
 var dialogbar;
 var dialogmask;
 DLGRESULT = null;  // Set this as soon as possible to avoid a background polling function reading a previously set value
 
 showDialog.buttonList = buttonList;  // Need to assign this here so that the Button List can be accessed in hideDialog() as per chilledflame's bug report

 if (!isDef(type)) {type = 'error';}
// if (!isDef(width)) {width = 0;}
// if (!isDef(height)) {height = 0;}
 if (!isDef(autohide)) {autohide = false;}
 if (!isDef(atcursor)) {atcursor = false;}
 if (!isDef(modal)) {modal = true;}
 if (!isDef(buttonList)) {buttonList = new Array();}
 
 if (!el('dialog')) 
 {
  dialog = document.createElement('div');
  dialog.id = 'dialog';
  dialogheader = document.createElement('div');
  dialogheader.id = 'dialog_header';
  dialogtitle = document.createElement('div');
  dialogtitle.id = 'dialog_title';
  dialogclose = document.createElement('div');
  dialogclose.id = 'dialog_close'
  dialogbody = document.createElement('div');
  dialogbody.id = 'dialog_body';
  dialogcontent = document.createElement('div');
  dialogcontent.id = 'dialog_content';
  dialogbar = document.createElement('div');
  dialogbar.id = 'dialog_bar';
  dialogmask = document.createElement('iframe');  // Fix for IE bug not overlaying <select> tags
  dialogmask.id = 'dialog_mask';
  dialogmask.style.display = 'none';  // Hide the mask before appending it to prevent flicker
  document.body.appendChild(dialogmask);
  document.body.appendChild(dialog);
  dialog.appendChild(dialogheader);
  dialogheader.appendChild(dialogtitle);
  dialogheader.appendChild(dialogclose);
  dialog.appendChild(dialogbody);
  dialogbody.appendChild(dialogcontent);
  dialogbody.appendChild(dialogbar);

  dialogclose.setAttribute('onclick','hideDialog()');
  dialogclose.onclick = hideDialog;

  dialogmask.setAttribute('frameborder', 0);
  var doc = dialogmask.contentDocument || dialogmask.contentWindow.document;
  doc.open();
  doc.writeln('<html><head></head><body style="margin: 0px"></body></html>');  // Create some mask content so that it can support an onclick event handler
  doc.close();
  doc.body.onclick = hideDialog;
  
  if (MASKCOLOR == '') MASKCOLOR = '#FFFFFF';  // Set default Dialog Mask background color to #FFFFFF
 }
 else 
 {
  dialog = el('dialog');
  dialogheader = el('dialog_header');
  dialogtitle = el('dialog_title');
  dialogclose = el('dialog_close');
  dialogbody = el('dialog_body');
  dialogcontent = el('dialog_content');
  dialogbar = el('dialog_bar');
  dialogmask = el('dialog_mask');
  dialog.style.visibility = "visible";
 }

 if (modal) dialogmask.style.display = 'block';

 dialogbar.style.display = ((buttonList.length > 0)?'block':'none');
 
 dialog.style.opacity = .00;
 dialog.style.filter = 'alpha(opacity=0)';
 dialog.alpha = 0;

 var content = el(WRAPPER);
 if (!isDef(content)) // WRAPPER should contain a valid id of a div or similar element which contains the page content
 {
  var docElem = document.documentElement;
  var docBody = document.body;
  content = ((docElem.clientHeight) ? docElem : docBody);
 }  
  
 dialog.className = type + "border";

 dialogheader.className = type + "header";
 dialogtitle.innerHTML = title;

 dialogbody.className = type + 'body';

 dialogbar.className = type + "border";
 
 dialogcontent.innerHTML = message;
 if (type == 'message') dialogcontent.style.overflow = 'auto';

 if (buttonList.length > 0)
 {
  if (buttonList.length == 1) {dialogbar.style.textAlign = 'center';}
  else {dialogbar.style.textAlign = 'right';}
 
  dialogbar.innerHTML = '';
  for (id in buttonList) {dialogbar.innerHTML += '<button type="button" onclick="hideDialog(' + id + ');">' + buttonList[id].name + '</button>';}
 }

 if (!isDef(width) || (isIE && width == 0)) dialog.style.width = DEFAULT_WIDTH + 'px';  // default setting (IE auto doesn't work so set to default)
 else
 {
  if (width == 0) dialog.style.width = 'auto';  // auto setting
  else dialog.style.width = width + 'px';
 } 

 if (!isDef(height) || (isIE && height == 0))  // default setting (IE auto doesn't work so set to default)
 {
  dialogcontent.style.height = DEFAULT_CONTENT_HEIGHT + 'px';
  dialog.style.height = (getHeight(dialogheader) + DEFAULT_CONTENT_HEIGHT + (isIE?0:13) + getHeight(dialogbar)) + 'px';  // + 2 * 6px content padding + 2 * 1px bar border
 } 
 else
 {
  if (height == 0)  // auto setting
  {
   dialogcontent.style.height = 'auto';
   dialog.style.height = 'auto';
  } 
  else
  {
   dialogcontent.style.height = (height - getHeight(dialogheader) - getHeight(dialogbar)) + 'px';
   dialog.style.height = (height + (isIE?0:13)) + 'px';  // + 2 * 6px content padding + 1px bar border
  } 
 } 

 var dialogtop, dialogleft;
 var contentwidth = getWidth(content), contentheight = getHeight(content);
 var dialogwidth = getWidth(dialog), dialogheight = getHeight(dialog);
 var scrollPos = getScrollPos();

 if (atcursor)
 {
  dialogtop = MouseMv.Y;   // Absolute to scrolled page
  dialogleft = MouseMv.X;  // Absolute to scrolled page

  if (!isIE)
  {
   dialogtop -= scrollPos.y;   // Relative to scrolled page
   dialogleft -= scrollPos.x;  // Relative to scrolled page
  }
 }
 else
 {
  dialogtop = ((contentheight - dialogheight) / 2);  // Position in center of WRAPPER
  dialogleft = ((contentwidth - dialogwidth) / 2);
 }
 
 if ((dialogtop + dialogheight) > contentheight - 2) dialogtop = contentheight - dialogheight - 2;  // Prevent dialog from being clipped by content bottom border
 if ((dialogleft + dialogwidth) > contentwidth - 2) dialogleft = contentwidth - dialogwidth - 2;    // Prevent dialog from being clipped by content right border
 dialogtop += scrollPos.y;
 dialogleft += scrollPos.x;

 dialog.style.top =  dialogtop + 'px';
 dialog.style.left = dialogleft + 'px';

 if (modal)
 {
  var scrollSize = getScrollSize();

  dialogmask.style.height = scrollSize.y + 'px';  
  dialogmask.style.width = scrollSize.x + 'px';   
  var doc = dialogmask.contentDocument || dialogmask.contentWindow.document;
  doc.body.style.height = dialogmask.style.height;  // Resize dialog mask body to full height of window so that it will capture an onclick event
  doc.body.style.backgroundColor = MASKCOLOR;
 }
 
 dialog.timer = setInterval("fadeDialog(1)", TIMER);

 if(autohide) 
 {
//  dialogclose.style.visibility = "hidden";
  window.setTimeout("hideDialog()", (autohide * 1000));
 }
// else {dialogclose.style.visibility = "visible";}
}
showDialog.buttonList = null;
 
// hide the dialog box //
function hideDialog(buttonId)
{
 var dialog = el('dialog');
 if (!isDef(buttonId)) buttonId = -1;
 DLGRESULT = buttonId;
 
 if (ALLOWHIDE || buttonId < 0 || showDialog.buttonList.length == 0 || showDialog.buttonList[buttonId].close)  // In some browsers buttonId is passed to the "hideDialog()" function with the onclick event value, otherwise it will default to 0
 {
  clearInterval(dialog.timer);
  dialog.timer = setInterval("fadeDialog(0)", TIMER);
 }
}

// fade-in the dialog box //
function fadeDialog(flag) 
{
 var dialog = el('dialog');
 var value;

 if (!isDef(flag)) {flag = 1;}
 
 if(flag == 1) {value = dialog.alpha + SPEED;} 
 else {value = dialog.alpha - SPEED;}
 
 dialog.alpha = value;
 dialog.style.opacity = (value / 100);
 dialog.style.filter = 'alpha(opacity=' + value + ')';

 if(value >= 99) 
 {
  clearInterval(dialog.timer);
  dialog.timer = null;
 } 
 else if(value <= 1) 
 {
  dialog.style.visibility = "hidden";
  if (el('dialog_mask')) el('dialog_mask').style.display = 'none';
  clearInterval(dialog.timer);
 }
}
